package core;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.ArrayList;
import java.util.Random;
import core.Material.PARTICLE_COLOR;

public class Particle
{
	private Random rnd;
	private Vector2D location;
	private Vector2D velocity;
	private Vector2D acceleration;
	private int lifetime;

	private float maxAccX;
	private float minAccX;
	private float maxAccY;
	private float minAccY;

	private int maxLifetime;
	private int minLifetime;
	private int maxLifetimeDiff;
	private int minLifetimeDiff;

	private int minVelocityX;
	private int maxVelocityX;
	private int minVelocityY;
	private int maxVelocityY;

	private boolean randomizeAccX;
	private boolean randomizeAccY;
	private boolean randomizeLifeTime;
	private boolean randomizeLifeTimeDiff;
	private boolean randomizeVelocityX;
	private boolean randomizeVelocityY;
	
	private boolean collision;

	private Material material;

	public Particle(Vector2D l)
	{
		rnd = new Random();
		material = null;
		location = new Vector2D(l);
		maxAccX = 0.2f;
		minAccX = -0.2f;
		maxAccY = 0.2f;
		minAccY = -0.2f;
		maxLifetime = 120;
		minLifetime = 275;
		maxLifetimeDiff = 2;
		minLifetimeDiff = 1;
		maxVelocityX = 2;
		minVelocityX = 0;
		maxVelocityY = 3;
		minVelocityY = 0;
		randomizeAccX = false;
		randomizeAccY = false;
		randomizeLifeTime = false;
		randomizeLifeTimeDiff = false;
		randomizeVelocityX = false;
		randomizeVelocityY = false;
		
		collision = true;
	}

	public void setAccelerationX(float min, float max)
	{
		if (min >= max && randomizeAccX)
		{
			maxAccX = 0.2f;
			minAccX = -0.2f;
		}
		else
		{
			maxAccX = max;
			minAccX = min;
		}
	}

	public void setAccelerationY(float min, float max)
	{
		if (min >= max && randomizeAccY)
		{
			maxAccY = 0.2f;
			minAccY = -0.2f;
		}
		else
		{
			maxAccY = max;
			minAccY = min;
		}
	}

	public void setLifetimeDiff(int min, int max)
	{
		if (min >= max && randomizeLifeTimeDiff)
		{
			maxLifetimeDiff = 2;
			minLifetimeDiff = 1;
		}
		else
		{
			maxLifetimeDiff = max;
			minLifetimeDiff = min;
		}
	}

	public void setLifetime(int min, int max)
	{
		if (min >= max && randomizeLifeTime)
		{
			maxLifetime = 275;
			minLifetime = 120;
		}
		else
		{
			maxLifetime = min;
			minLifetime = max;
		}
	}

	public void setVelocityX(int min, int max)
	{
		if (min >= max && randomizeVelocityX)
		{
			maxVelocityX = 2;
			minVelocityX = 0;
		}
		else
		{
			maxVelocityX = max;
			minVelocityX = min;
		}
	}

	public void setVelocityY(int min, int max)
	{
		if (min >= max && randomizeVelocityY)
		{
			maxVelocityY = 3;
			minVelocityY = 0;
		}
		else
		{
			maxVelocityY = max;
			minVelocityY = min;
		}
	}
	
	public void setCollisionEnabled(boolean c)
	{
		collision = c;
	}

	public void setRandomizeAccelerationX(boolean a)
	{
		randomizeAccX = a;
	}

	public void setRandomizeAccelerationY(boolean a)
	{
		randomizeAccY = a;
	}

	public void setRandomizeVelocityX(boolean a)
	{
		randomizeVelocityX = a;
	}

	public void setRandomizeVelocityY(boolean a)
	{
		randomizeVelocityY = a;
	}

	public void setRandomizeLifetime(boolean a)
	{
		randomizeLifeTime = a;
	}

	public void setRandomizeLifetimeDiff(boolean a)
	{
		randomizeLifeTimeDiff = a;
	}

	public void create()
	{
		float max = randomizeAccX ? (float) randomInRange(minAccX, maxAccX) : minAccX;
		float may = randomizeAccY ? (float) randomInRange(minAccY, maxAccY) : minAccY;
		float mvx = randomizeVelocityX ? randomInRange(minVelocityX, maxVelocityX) : minVelocityX;
		float mvy = randomizeVelocityY ? randomInRange(minVelocityY, maxVelocityY) : minVelocityY;

		acceleration = new Vector2D(max, may);
		velocity = new Vector2D(mvx, mvy);
		lifetime = randomizeLifeTime ? randomInRange(minLifetime, maxLifetime) : minLifetime;
	}

	public void setMaterial(Material mat)
	{
		material = mat;
	}

	private double randomInRange(float min, float max)
	{
		return Math.random() < 0.5 ? ((1 - Math.random()) * (max - min) + min) : (Math.random() * (max - min) + min);
	}

	private int randomInRange(int min, int max)
	{
		return rnd.nextInt(max - min) + min;
	}

	public void update(int w, int h, ArrayList<Rectangle> obst)
	{
		velocity = velocity.plus(acceleration);
		location = location.plus(velocity);
		lifetime -= (randomizeLifeTimeDiff ? randomInRange(minLifetimeDiff, maxLifetimeDiff) : minLifetimeDiff);

		if (!collision)
			return;
		
		boolean b = false;
		for (Rectangle r : obst)
		{
			if (location.x > r.x - 6 && location.x < r.x + r.width && location.y > r.y - 6 && location.y < r.y + r.height)
			{
				velocity.x /= 2;
				velocity.x = -velocity.x;
				b = true;
			}
			if (location.y > r.y - 6 && location.y < r.y + r.height && location.x > r.x - 6 && location.x < r.x + r.width)
			{
				velocity.y /= 2;
				velocity.y = -velocity.y;
				b = true;
			}
			if (b)
				break;
		}

	}

	public void setColor(PARTICLE_COLOR c)
	{
		if (c != null)
			material.color = c;
	}

	private void changeGraphicsColor(Graphics g)
	{
		switch (material.color)
		{
			case BLUE:
				g.setColor(new Color(0, 0, 1.0f, isDead() ? 0 : (lifetime < 256 ? lifetime : 255) / 255.0f));
				break;
			case GREEN:
				g.setColor(new Color(0, 1.0f, 0, isDead() ? 0 : (lifetime < 256 ? lifetime : 255) / 255.0f));
				break;
			case ORANGE:
				g.setColor(new Color(1.0f, 0.5f, 0, isDead() ? 0 : (lifetime < 256 ? lifetime : 255) / 255.0f));
				break;
			case YELLOW:
				g.setColor(new Color(1.0f, 1.0f, 0, isDead() ? 0 : (lifetime < 256 ? lifetime : 255) / 255.0f));
				break;
			case RED:
				g.setColor(new Color(1.0f, 0, 0, isDead() ? 0 : (lifetime < 256 ? lifetime : 255) / 255.0f));
				break;
			case INDIGO:
				g.setColor(new Color(75, 0, 130, isDead() ? 0 : (lifetime < 256 ? lifetime : 255)));
				break;
			case VIOLET:
				g.setColor(new Color(143, 0, 255, isDead() ? 0 : (lifetime < 256 ? lifetime : 255)));
				break;
		}
	}

	public void draw(Graphics g)
	{
		if (material == null)
			return;

		if (!material.isTextured())
		{
			changeGraphicsColor(g);
			int sx = randomInRange(1, 4);
			int sy = randomInRange(1, 4);
			material.shapeSize = new Point(sx, sy);
		}
		else
			material.opacity = lifetime / 255.0f;

		material.drawTexture(g, (int) location.x, (int) location.y);
	}

	public boolean isDead()
	{
		return lifetime < 0;
	}
}
